package org.wu.framework.bean.definition;

import org.wu.framework.bean.Injecting;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;

/**
 * 声明的class 缓存
 */
public class DefineClassCacheFactory {


    // 需要初始化的class
    private static final LinkedHashSet<Class<?>> beanClasses = new LinkedHashSet<>();
    // 缓存本地class对象声明定义
    private static final ConcurrentHashMap<Class<?>, BeanDefinition> BEAN_DEFINITION_CONCURRENT_HASH_MAP = new ConcurrentHashMap<>();


    /**
     * 缓存 clazz
     *
     * @param clazz 缓存的clazz
     */
    public void cache(Class<?> clazz) {
        beanClasses.add(clazz);
    }

    /**
     * 缓存 clazz
     *
     * @param clazzList 缓存的clazz
     */
    public void cache(Collection<Class<?>> clazzList) {
        beanClasses.addAll(clazzList);
    }


    /**
     * 缓存 class 为 BeanDefinition
     *
     * @param clazz class
     * @return BeanDefinition
     */
    public static BeanDefinition cacheBeanDefinition(Class<?> clazz) {


        if (BEAN_DEFINITION_CONCURRENT_HASH_MAP.containsKey(clazz)) {
            return BEAN_DEFINITION_CONCURRENT_HASH_MAP.get(clazz);
        }
        // 获取构造方法
        Constructor<?>[] constructors = clazz.getConstructors();

        if (constructors.length > 1) {
            throw new IllegalArgumentException("the bean of " + clazz.getName() + " must has on public  Constructor  but found " + constructors.length);
        }
        Constructor<?> constructor = constructors[0];
        GeneralBeanDefinition generalBeanDefinition = new GeneralBeanDefinition();
        generalBeanDefinition.setConstructor(constructor);
        generalBeanDefinition.setParameterTypes(constructor.getParameterTypes());

        // 字段
        List<Field> injectingFields = injectingFields(clazz);
        generalBeanDefinition.setInjectingFields(injectingFields);

        // 方法

        List<Method> injectingMethods = injectingMethods(clazz);
        generalBeanDefinition.setInjectingMethods(injectingMethods);

        // init方法
        List<Method> initMethods = initMethods(clazz);
        generalBeanDefinition.setInitMethods(initMethods);

        BEAN_DEFINITION_CONCURRENT_HASH_MAP.put(clazz, generalBeanDefinition);
        return generalBeanDefinition;
    }


    /**
     * 获取 注入字段
     *
     * @param clazz clazz
     * @return 返回注入字段
     */
    public static List<Field> injectingFields(Class<?> clazz) {
        List<Field> injectingFields = new ArrayList<>();

        Field[] declaredFields = clazz.getDeclaredFields();
        Field[] fields = clazz.getFields();

        Field[] fieldsArray = new Field[declaredFields.length + fields.length];
        System.arraycopy(declaredFields, 0, fieldsArray, 0, declaredFields.length);
        System.arraycopy(fields, 0, fieldsArray, declaredFields.length, fields.length);
        for (Field field : fieldsArray) {
            boolean annotationPresent = field.isAnnotationPresent(Injecting.class);
            if (annotationPresent) {
                injectingFields.add(field);
            }
        }
        return injectingFields;
    }

    /**
     * 获取 注入方法
     *
     * @param clazz clazz
     * @return 返回注入方法
     */
    public static List<Method> injectingMethods(Class<?> clazz) {
        List<Method> injectingMethods = new ArrayList<>();

        Method[] declaredMethods = clazz.getDeclaredMethods();
        Method[] methods = clazz.getMethods();


        Method[] methodArray = new Method[declaredMethods.length + methods.length];
        System.arraycopy(declaredMethods, 0, methodArray, 0, declaredMethods.length);
        System.arraycopy(methods, 0, methodArray, declaredMethods.length, methods.length);
        for (Method method : methodArray) {
            boolean annotationPresent = method.isAnnotationPresent(Injecting.class);
            if (annotationPresent) {
                injectingMethods.add(method);
            }
        }
        return injectingMethods;
    }

    /**
     * 获取 init方法
     *
     * @param clazz clazz
     * @return init方法
     */
    private static List<Method> initMethods(Class<?> clazz) {
        List<Method> initMethods=new ArrayList<>();

        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            boolean annotationPresent = method.isAnnotationPresent(Injecting.class);
            if (annotationPresent) {
                initMethods.add(method);
            }
        }
        return initMethods;
    }


}
